#define PrynInternal
#define PrynImplementation
#define PrynGuiInternal
#include <prynGui.h>
#include <pryn.h>

static PrynResult CommonObject (const PrynObject *object, PrynCommonObject **output)
{
#define caseNone(NAME) case PrynObjectType##NAME: return PrynResultDone;
#define caseSimple(NAME) case PrynObjectType##NAME: if (output) *output = &((Pryn##NAME *) object->pointer)->pCommonObject; return PrynResultSuccess

	PrynStartFunction ();

	if (output) *output = 0;
	switch (object->type)
	{
		caseNone (None);
		caseNone (Pointer);
		caseNone (String);
		caseNone (Value);
		caseSimple (State);
		caseSimple (Factory);
		caseSimple (Library);
		caseSimple (Mediator);
		caseSimple (Monitor);
		caseSimple (Pin);
		caseSimple (Tag);
		caseSimple (Type);
		caseSimple (Component);
	}

	prynReturnFailure (UnknownEnumeration);
	PrynEndFunction ();

#undef caseSimple
#undef caseNone
}

PrynExport (PrynResult, prynObjectScope, (const PrynObject *objectPointer, PrynObject *scope))
{
	PrynObject object, output = {0, 0};

	if (objectPointer) object = *objectPointer;
	if (scope) *scope = output;
	if (!objectPointer) return PrynResultNullArgument;

	switch (object.type)
	{
		case PrynObjectTypePointer:
		case PrynObjectTypeString:
		case PrynObjectTypeNone:
		case PrynObjectTypeState:
			return PrynResultDone;

		#define returnField(NAME, SCOPE, FIELD) \
			do { \
				output.type = PrynObjectType##SCOPE; \
				output.pointer = ((Pryn##NAME *) object.pointer)->FIELD; \
				if (scope) *scope = output; \
				return PrynResultSuccess; \
			} while (0)

		#define caseField(NAME, SCOPE, FIELD) \
			case PrynObjectType##NAME: \
				returnField (NAME, SCOPE, FIELD)

		#define caseMediated(NAME) \
			case PrynObjectType##NAME: \
			{ \
				Pryn##NAME *self = (Pryn##NAME *) object.pointer; \
				if (self->pMediator) \
					returnField (NAME, Mediator, pMediator); \
				returnField (NAME, Library, pLibrary); \
			}

		#define caseObjectField(NAME, FIELD) \
			case PrynObjectType##NAME: \
				if (scope) *scope = ((Pryn##NAME *) object.pointer)->FIELD; \
				return PrynResultSuccess

		caseMediated (Factory);
		caseMediated (Type);

		caseObjectField (Monitor, pObject);
		caseObjectField (Tag, pObject);

		caseField (Component, Factory, pFactory);
		caseField (Library, State, pState);
		caseField (Mediator, Library, pLibrary);
		caseField (Pin, Component, pComponent);
		caseField (Value, Type, pType);
		caseField (GuiControl, State, pState);
		#undef caseField
		#undef caseObjectField
		#undef caseMediated
		#undef returnField
	}

	return PrynResultUnknownEnumeration;
}

PrynExport (PrynResult, prynObjectState, (const PrynObject *object, PrynState **output))
{
	if (output) *output = 0;
	if (!object) return PrynResultNullArgument;

	switch (object->type)
	{
		case PrynObjectTypePointer:
		case PrynObjectTypeString:
		case PrynObjectTypeNone:
			return PrynResultFalse;

		case PrynObjectTypeState: *output = (PrynState *) object->pointer; return PrynResultSuccess;
		#define case(NAME) case PrynObjectType##NAME: return pryn##NAME##State ((Pryn##NAME *) object->pointer, output)
		case (Component);
		case (Factory);
		case (Library);
		case (Mediator);
		case (Monitor);
		case (Pin);
		case (Tag);
		case (Type);
		case (Value);
		#undef case
		case PrynObjectTypeGuiControl: *output = ((PrynGuiControl *) object->pointer)->pState; return PrynResultSuccess;
	}

	return PrynResultUnknownEnumeration;
}

PrynExport (PrynResult, prynObjectTags, (const PrynObject *object, PrynTagList **output))
{
	PrynStartFunction ();
	PrynCommonObject *common;

	if (output) *output = 0;
	prynCall (CommonObject (object, &common));
	if (output) *output = &common->pTags;

	PrynEndFunction ();
}

PrynExport (PrynResult, prynObjectMonitors, (const PrynObject *object, PrynMonitorList **output))
{
	PrynStartFunction ();
	PrynCommonObject *common;

	if (output) *output = 0;
	prynCall (CommonObject (object, &common));
	if (output) *output = &common->pMonitors;

	PrynEndFunction ();
}

#define prynStateState(STATE, PSTATE) ((*(PSTATE)) = (STATE))
#define prynGuiControlState(CONTROL, STATE) (*(STATE) = (CONTROL)->pState)

#define PrynObjectToAndFrom(TYPE) \
	PrynExport (PrynResult, pryn##TYPE##ToObject, (Pryn##TYPE *value, PrynObject *output)) \
	{ \
		PrynObject null = { 0, 0 }; \
		PrynState *state = 0; \
		if (output) *output = null; \
		pryn##TYPE##State (value, &state); \
		if (!value || !output) return PrynResultNullArgument; \
		output->type = PrynObjectType##TYPE; \
		output->pointer = value; \
		return PrynResultSuccess; \
	} \
	\
	PrynExport (PrynResult, prynObjectTo##TYPE, (PrynObject *object, Pryn##TYPE **output)) \
	{ \
		PrynState *state = 0; \
		if (output) *output = 0; \
		prynObjectState (object, &state); \
		if (!output || !object) return PrynResultNullArgument; \
		if (object->type != PrynObjectType##TYPE) return PrynResultBadArgument; \
		*output = (Pryn##TYPE *) object->pointer; \
		return PrynResultSuccess; \
	}

PrynObjectToAndFrom (Component)
PrynObjectToAndFrom (Factory)
PrynObjectToAndFrom (Library)
PrynObjectToAndFrom (Mediator)
PrynObjectToAndFrom (Monitor)
PrynObjectToAndFrom (Pin)
PrynObjectToAndFrom (State)
PrynObjectToAndFrom (Tag)
PrynObjectToAndFrom (Type)
PrynObjectToAndFrom (Value)

PrynObjectToAndFrom (GuiControl)